package emulator.hardware.io.peripherals;

import java.util.Observable;
import java.util.Observer;

import emulator.hardware.bits.SharedBit;
import emulator.hardware.io.Port6522;
import emulator.hardware.io.Port6522Listener;
import emulator.hardware.io.Via6522;

public class VicTape {
	private VicTapeState tape_state;
	private SharedBit motor_out;
	private SharedBit switch_in;
	private boolean do_record = false;
	private long clocks_per_sec = 4433618/4;
	
	public void attachToVia(Via6522 via_keyboard, Via6522 via_joystick)
	{
		tape_state = new VicTapeState();
		
		motor_out = via_joystick.getPortA().getControlBit(1);
		switch_in = via_joystick.getPortA().getInPortBit(6);
		
		via_keyboard.getPortA().addPortListener(new CassetteReadListener());
		via_keyboard.getPortB().addPortListener(new CassetteWriteListener());
		
		addMotorObserver(new MotorStateObserver());
	}
	
	public void setSwitch(boolean enable, boolean do_record)
	{
		switch_in.setValue(enable);
		this.do_record = do_record;
		updateState();
	}
	
	public void addMotorObserver(Observer o)
	{
		motor_out.addObserver(o);
	}
	
	public void rewind() {
		tape_state.getContents().rewind();
	}

	public void forward() {
		tape_state.getContents().forward();
	}

	public int getCurrentTrack() {
		return tape_state.getContents().getCurrentIndex();
	}

	public int getTrackCount() {
		return tape_state.getContents().getTrackCount();
	}

	public String getCurrentLength() {
		long ms = tape_state.getContents().getCurrentTrack().getCount() / (1000*clocks_per_sec);
		long secs = ms / 1000;
		long minutes = secs / 60;
		ms %= 1000;
		secs %= 60;
		String min_str = "00" + Long.toString(minutes);
		min_str = min_str.substring(min_str.length()-2);
		String sec_str = "00" + Long.toString(secs);
		sec_str = sec_str.substring(sec_str.length()-2);
		String ms_str = "000" + Long.toString(ms);
		ms_str = ms_str.substring(ms_str.length()-3);
		return min_str+":"+sec_str+"."+ms_str;
	}

	private void updateState() {
		boolean tape_running = 
			(motor_out.getValue() == false) && 
			(switch_in.getValue() == false);
		if (tape_running)
		{
			if (do_record)
				tape_state.handleRecordEvent();
			else
				tape_state.handlePlayEvent();
		}
		else
			tape_state.handleStopEvent();
	}

	class MotorStateObserver implements Observer
	{
		@Override
		public void update(Observable o, Object arg) {
			updateState();
		}
	}
	
	class CassetteReadListener implements Port6522Listener
	{

		@Override
		public void handlePre(Port6522 port) {
			port.getControlBit(0).setValue(tape_state.performPreReadEvent(port.getControlBit(0).getValue()));
		}

		@Override
		public void handlePost(Port6522 port) {
			port.getControlBit(0).setValue(tape_state.performPostReadEvent(port.getControlBit(0).getValue()));
		}
	}
	
	class CassetteWriteListener implements Port6522Listener
	{
		@Override
		public void handlePre(Port6522 port) {
			port.getOutPortBit(3).setValue(tape_state.performPreWriteEvent(port.getOutPortBit(3).getValue()));
		}

		@Override
		public void handlePost(Port6522 port) {
			port.getOutPortBit(3).setValue(tape_state.performPostWriteEvent(port.getOutPortBit(3).getValue()));
		}
	}
}
